/* -*-c++-*- */
/* osgEarth - Geospatial SDK for OpenSceneGraph
 * Copyright 2020 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
 */

#ifndef OSGEARTHSYMBOLOGY_EXPRESSION_H
#define OSGEARTHSYMBOLOGY_EXPRESSION_H 1

#include <osgEarth/Common>
#include <osgEarth/Config>
#include <osgEarth/URI>
#include <osgEarth/GeoData>
#include <osgEarth/TileKey>

namespace osgEarth
{    
    /**
     * Simple numeric expression evaluator with variables.
     */
    class OSGEARTH_EXPORT NumericExpression
    {
    public:
        typedef std::pair<std::string,unsigned> Variable;
        typedef std::vector<Variable> Variables;

    public:
        NumericExpression();

        NumericExpression( const Config& conf );

        /** Construct a new expression from the infix string. */
        NumericExpression( const std::string& expr );

        /** Construct a new static expression from a value */
        NumericExpression( double staticValue );

        /** Copy ctor. */
        NumericExpression( const NumericExpression& rhs );

        /** dtor */
        virtual ~NumericExpression() { }

        /** Set the result to a literal value. */
        void setLiteral( double staticValue );

        /** Access the expression variables. */
        const Variables& variables() const { return _vars; }

        /** Set the value of a variable. */
        void set( const Variable& var, double value );

        /** Evaluate the expression. */
        double eval() const;

        /** Gets the expression string. */
        const std::string& expr() const { return _src; }

        /** Whether the expression is empty */
        bool empty() const { return _src.empty(); }

    public:
        Config getConfig() const;
        void mergeConfig( const Config& conf );

    private:
        enum Op { OPERAND, VARIABLE, ADD, SUB, MULT, DIV, MOD, MIN, MAX, LPAREN, RPAREN, COMMA }; // in low-high precedence order
        typedef std::pair<Op,double> Atom;
        typedef std::vector<Atom> AtomVector;
        typedef std::stack<Atom> AtomStack;
        
        std::string _src;
        AtomVector  _rpn;
        Variables   _vars;
        double      _value;
        bool        _dirty;

        void init();
    };

    //--------------------------------------------------------------------

    /**
     * Simple string expression evaluator with variables.
     */
    class OSGEARTH_EXPORT StringExpression
    {
    public:
        typedef std::pair<std::string,unsigned> Variable;
        typedef std::vector<Variable> Variables;

    public:
        StringExpression();

        StringExpression( const Config& conf );

        /** Construct a new expression from the infix string. */
        StringExpression( const std::string& expr );

        /** Construct an expression from the infix string and a URI context. */
        StringExpression( const std::string& expr, const URIContext& uriContext );

        /** Copy ctor. */
        StringExpression( const StringExpression& rhs );

        /** dtor */
        virtual ~StringExpression() { }

        /** Set the infix expr. */
        void setInfix( const std::string& infix );

        /** Set the infix expr to a literal string */
        void setLiteral( const std::string& value );

        /** Access the expression variables. */
        const Variables& variables() const { return _vars; }

        /** Set the value of a variable. */
        void set( const Variable& var, const std::string& value );

        /** Set the value of a names variable if it exists */
        void set( const std::string& varName, const std::string& value );

        /** Evaluate the expression. */
        const std::string& eval() const;

        /** Evaluate the expression as a URI. 
            TODO: it would be better to have a whole new subclass URIExpression */
        URI evalURI() const;

        /** Gets the expression string. */
        const std::string& expr() const { return _src; }

        /** Whether the expression is empty */
        bool empty() const { return _src.empty(); }

        void setURIContext( const URIContext& uriContext ) { _uriContext = uriContext; }
        const URIContext& uriContext() const { return _uriContext; }

    public:
        Config getConfig() const;
        void mergeConfig( const Config& conf );

    private:
        enum Op { OPERAND, VARIABLE }; // in low-high precedence order
        typedef std::pair<Op,std::string> Atom;
        typedef std::vector<Atom> AtomVector;
        
        std::string  _src;
        AtomVector   _infix;
        Variables    _vars;
        std::string  _value;
        bool         _dirty;
        URIContext   _uriContext;

        void init();
    };
} // namespace osgEarth

OSGEARTH_SPECIALIZE_CONFIG(osgEarth::NumericExpression);
OSGEARTH_SPECIALIZE_CONFIG(osgEarth::StringExpression);

#endif // OSGEARTHSYMBOLOGY_EXPRESSION_H
